#define Delegate_Template_Params    Make_Params1(Delegate_Num_Args, class T)
// class T0, class T1, class T2, ...
#define Delegate_Template_Args      Make_Params1(Delegate_Num_Args, T)
// T0, T1, T2, ...
#define Delegate_Function_Params    Make_Params2(Delegate_Num_Args, T, a)
// T0 a0, T1 a1, T2 a2, ...
#define Delegate_Function_Args      Make_Params1(Delegate_Num_Args, a)
// a0, a1, a2, ...

#if Delegate_Num_Args == 0
#define Delegate_Comma
#else
#define Delegate_Comma ,
#endif

namespace delegate
{
	template<class R Delegate_Comma Delegate_Template_Params,bool Order,class LockPolicy >
	class Delegate<R (Delegate_Template_Args),Order,LockPolicy>
	{
	public:
		typedef Delegate<R (Delegate_Template_Args),Order,LockPolicy> self;
	public:
		Delegate()
			:functions_(0)
		{
		}

		template <class T>
		Delegate(const T& f)
			:functions_(0)
		{
			*this = f;
		}

		template<class O, class F>
		Delegate(const O& o, const F& f)
			:functions_(0)
		{
			*this = std::make_pair(o, f);
		}

		template <class T>
		Delegate& operator =(const T& f)
		{
			clean();
			add(f);
			return *this;
		}

		template <class T>
		Delegate& operator +=(const T& f)
		{
			add(f);
			return *this;
		}

		template <class T>
		Delegate& operator -=(const T& f)
		{
			remove(f);
			return *this;
		}

		~Delegate()
		{
			clean();
			if ( 0 != functions_ )
			{
				delete functions_;
			}
		}

		void clean()
		{
			if ( 0 == functions_ )
			{
				return;
			}

			typename threadmutex::Lock<LockPolicy> lock(lock_);
			for (typename callback_array::iterator it = functions_->begin();
				it!=functions_->end();it++)
			{
				if (0 != (*it))
				{
					delete (*it);
					*it = 0;
				}
			}
			functions_->clear();
		}

		R operator () (Delegate_Function_Params)
		{
			try
			{
				return Partial<R,0,Order>::run(Delegate_Function_Args Delegate_Comma this);
			}
			catch (InvalidCallBackException)
			{
				throw;
			}
		}
	private:
		template<class Ret,size_t Dummy, bool OrderP>
		struct Partial
		{
			static Ret run(Delegate_Function_Params Delegate_Comma self* other)
			{
				if (0 == other->functions_ )
				{
					return InvalidCall<Ret>();
				}

				typename threadmutex::Lock<LockPolicy> lock(other->lock_);

				if (other->functions_->empty())
				{
					return InvalidCall<Ret>();
				}

				if (!OrderP)
				{
					reverse(other->functions_->begin(), other->functions_->end());
				}

				size_t size = other->functions_->size();
				ICallBack* callback = 0;
				for (size_t i = 0; i < size-1 ; i++)
				{
					callback = other->functions_->at(i);
					if(0!=callback)
					{
						callback->invoke(Delegate_Function_Args);
					}
				}
				callback = other->functions_->at(size-1);
				if(0==callback)
				{
					return InvalidCall<Ret>();
				}
				return callback->invoke(Delegate_Function_Args);
			}
		};

		template<size_t Dummy,bool OrderP>
		struct Partial<void,Dummy,OrderP>
		{
			static void run(Delegate_Function_Params Delegate_Comma self* other)
			{
				if (0 == other->functions_)
				{
					return InvalidCall<void>();
				}

				typename threadmutex::Lock<LockPolicy> lock(other->lock_);

				if (!OrderP)
				{
					reverse(other->functions_->begin(), other->functions_->end());
				}

				for (typename callback_array::iterator it = other->functions_->begin();
					it!=other->functions_->end();it++)
				{
					if (0 != (*it))
					{
						(*it)->invoke(Delegate_Function_Args);
					}
				}
			}
		};

		template <class T>
		struct Invoker
		{
			static R invoke(const T& f Delegate_Comma Delegate_Function_Params)
			{
				return (const_cast<T&>(f))(Delegate_Function_Args);
			}
		};

		template <class O,class F>
		struct Invoker<std::pair<O,F> >
		{
			static R invoke(const std::pair<O,F>& mf Delegate_Comma Delegate_Function_Params)
			{
				return ((*mf.first).*mf.second)(Delegate_Function_Args);
			}
		};

		class ICallBack
		{
		public:
			virtual ~ICallBack() { }
			virtual R invoke(Delegate_Function_Params) const = 0;
		};

		template <class T>
		class CallBack : public ICallBack
		{
		public:
			CallBack(const T& f)
				:functor_(f)
			{
			}
			CallBack(const CallBack& other)
				:functor_(other.functor_)
			{
			}
			~CallBack()
			{
			}
			virtual R invoke(Delegate_Function_Params) const
			{
				return Invoker<T>::invoke(this->functor_ Delegate_Comma Delegate_Function_Args);
			}
			T functor_;
		};

	private:
		typedef std::vector<ICallBack*> callback_array;

		template <class T>
		void add(const T& f)
		{
			check();
			typename threadmutex::Lock<LockPolicy> lock(lock_);
			// if (find(f)==functions_->end())
			{
				ICallBack* d = new CallBack<T>(f);
				functions_->push_back(d);
			}
		}

		template <class T>
		typename callback_array::iterator find(const T& f)
		{
			CallBack<T> cal(f);
			typename callback_array::iterator iter = functions_->begin();
			for (;iter!=functions_->end();iter++)
			{
				CallBack<T>* p = dynamic_cast<CallBack<T>*>(*iter);
				// 0 mean:it could be other type,or something,so must be different.
				if (0!=p && p->functor_ == cal.functor_)
				{
					break;
				}
			}
			return iter;
		}

		template <class T>
		void remove(const T& f)
		{
			if ( 0 == functions_ )
			{
				return;
			}

			typename threadmutex::Lock<LockPolicy> lock(lock_);
			if (functions_->empty())
			{
				return;
			}

			typename callback_array::iterator iter = find(f);
			if (iter!=functions_->end())
			{
				CallBack<T>* p = dynamic_cast<CallBack<T>*>(*iter);
				if (0!=p)
				{
					delete p;
					p = 0;
				}
				functions_->erase(iter);
			}
		}

		inline void check()
		{
			if ( 0 == functions_ )
			{
				functions_ = new callback_array();
			}
		}
	private:
		callback_array* functions_;
		LockPolicy lock_;
	};}

#undef Delegate_Template_Params
#undef Delegate_Template_Args
#undef Delegate_Function_Params
#undef Delegate_Function_Args
#undef Delegate_Comma
